﻿/*
   Copyright 2013 Dmitry Bratus

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using AopAlliance.Intercept;
using NUnit.Framework;
using Spring.Aop;
using Spring.Aop.Support;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace Flower.Services.Clients.Tests
{
    [TestFixture]
    public class WcfClientFactoryTests
    {
        [ServiceContract]
        public interface IService
        {
            [OperationContract]
            void Increment();

            [OperationContract]
            string Echo(string message);
        }

        [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
        public class Service : IService
        {
            public int Counter;

            public void Increment()
            {
                ++Counter;
            }

            public string Echo(string message)
            {
                return message;
            }
        }

        [Test]
        public void CreateSingleClient()
        {
            var serviceHost = new ServiceHost(new Service());

            try
            {
                var binding = new NetNamedPipeBinding();
                var address = "net.pipe://localhost/IService";

                serviceHost.AddServiceEndpoint(typeof(IService), binding, address);
                serviceHost.Open();

                var factory = new WcfClientFactory<IService>();
                var client = factory.Create(binding, address);

                Assert.AreEqual("Message", client.Echo("Message"));
            }
            finally
            {
                try
                {
                    serviceHost.Close();
                }
                catch (Exception) { }
            }
        }

        [Test]
        public void BalancingClientTest()
        {
            var services = new[]
            {
                new Service(),
                new Service()
            };
            var serviceHosts = new[]
            {
                new ServiceHost(services[0]),
                new ServiceHost(services[1])
            };

            try
            {
                var binding = new NetNamedPipeBinding();
                var address = "net.pipe://localhost/IService";

                for (int i = 0; i < services.Length; ++i)
                {
                    serviceHosts[i].AddServiceEndpoint(typeof(IService), binding, address + i);
                    serviceHosts[i].Open();
                }

                var factory = new WcfClientFactory<IService>();
                var endpoints = new List<IService>();

                for (int i = 0; i < services.Length + 1; ++i)
                {
                    endpoints.Add
                    (
                        factory.Create(binding, address + i)
                    );
                }
                var client = factory.CreateBalancingClient(endpoints);

                for (int i = 0; i < services.Length * 3; ++i)
                {
                    client.Increment();
                }

                Assert.AreEqual(services.Length * 3, services[0].Counter + services[1].Counter);
                Assert.IsTrue(services[0].Counter != services[1].Counter);
            }
            finally
            {
                foreach (var serviceHost in serviceHosts)
                {
                    try
                    {
                        serviceHost.Close();
                    }
                    catch (Exception) { }
                }
            }
        }

        [Test]
        public void FailoverClientTest()
        {
            var services = new[]
            {
                new Service(),
                new Service()
            };
            var serviceHosts = new[]
            {
                new ServiceHost(services[0]),
                new ServiceHost(services[1])
            };

            try
            {
                var binding = new NetNamedPipeBinding();
                var address = "net.pipe://localhost/IService";

                for (int i = 0; i < services.Length; ++i)
                {
                    serviceHosts[i].AddServiceEndpoint(typeof(IService), binding, address + (i + 1));
                    serviceHosts[i].Open();
                }

                var factory = new WcfClientFactory<IService>();
                var endpoints = new List<IService>();

                for (int i = 0; i < services.Length + 1; ++i)
                {
                    endpoints.Add
                    (
                        factory.Create(binding, address + i)
                    );
                }
                var client = factory.CreateFailoverClient(endpoints);

                for (int i = 0; i < services.Length * 3; ++i)
                {
                    client.Increment();
                }

                Assert.AreEqual(services.Length * 3, services[0].Counter);
            }
            finally
            {
                foreach (var serviceHost in serviceHosts)
                {
                    try
                    {
                        serviceHost.Close();
                    }
                    catch (Exception) { }
                }
            }
        }

        [Test]
        public void BroadcastClientTest()
        {
            var services = new[]
            {
                new Service(),
                new Service()
            };
            var serviceHosts = new[]
            {
                new ServiceHost(services[0]),
                new ServiceHost(services[1])
            };

            try
            {
                var binding = new NetNamedPipeBinding();
                var address = "net.pipe://localhost/IService";

                for (int i = 0; i < services.Length; ++i)
                {
                    serviceHosts[i].AddServiceEndpoint(typeof(IService), binding, address + i);
                    serviceHosts[i].Open();
                }

                var factory = new WcfClientFactory<IService>();
                var endpoints = new List<IService>();

                for (int i = 0; i < services.Length; ++i)
                {
                    endpoints.Add
                    (
                        factory.Create(binding, address + i)
                    );
                }
                var client = factory.CreateBroadcastClient(endpoints);

                for (int i = 0; i < 3; ++i)
                {
                    client.Increment();
                }

                Assert.AreEqual(3, services[0].Counter);
                Assert.AreEqual(3, services[1].Counter);
            }
            finally
            {
                foreach (var serviceHost in serviceHosts)
                {
                    try
                    {
                        serviceHost.Close();
                    }
                    catch (Exception) { }
                }
            }
        }

        [Test]
        public void DynamicWcfClientFactoryTest()
        {
            var factory = new WcfClientFactory
            {
                Contract = typeof(IService).AssemblyQualifiedName
            };
            var binding = new NetNamedPipeBinding();
            var address = "net.pipe://localhost/IService";
            var endpointAddress = new EndpointAddress(address);
            var config = "ConfigurationName";

            Assert.IsTrue(factory.Create(config) is IService);
            Assert.IsTrue(factory.Create(config, address) is IService);
            Assert.IsTrue(factory.Create(config, endpointAddress) is IService);
            Assert.IsTrue(factory.Create(binding, address) is IService);
            Assert.IsTrue(factory.Create(binding, endpointAddress) is IService);

            var services = new[]
            {
                factory.Create(config),
                factory.Create(config),
                factory.Create(config)
            };

            Assert.IsTrue(factory.CreateBalancingClient(services) is IService);
            Assert.IsTrue(factory.CreateFailoverClient(services) is IService);
        }

        private class CounterInterceptor : IMethodInterceptor
        {
            public int Count;

            public object Invoke(IMethodInvocation invocation)
            {
                ++Count;
                return invocation.Proceed();
            }
        }

        [Test]
        public void CustomAdvisorsTest()
        {
            var advice = new CounterInterceptor();
            var factory = new WcfClientFactory
            {
                Contract = typeof(IService).AssemblyQualifiedName,
                Advisors = new List<IAdvisor>
                {
                    new DefaultPointcutAdvisor
                    {
                        Advice = advice
                    }
                }
            };
            var binding = new NetNamedPipeBinding();
            var address = "net.pipe://localhost/IService";
            var endpointAddress = new EndpointAddress(address);

            var client = (IService)factory.Create(binding, address);

            try { client.Increment(); } catch { }

            Assert.AreEqual(1, advice.Count);
        }
    }
}
